using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Input.Touch;
using Services;
using Scopa.GameSound;


namespace Scopa.GamePlay
{
  public enum RenderState
  {
    Intro,
    Input,
    Animation,
    TurnStart,
    ShowLastTake,
    Outro,
    Blurred,
    Recap,
    PauseMenu
  }

  /// <summary>
  /// This is a game component that implements IUpdateable.
  /// </summary>
  public abstract class BaseRenderer : Microsoft.Xna.Framework.DrawableGameComponent, IBoardRenderer
  {
    protected SpriteBatch spriteBatch;
    protected Matrix screen_transform;
    protected RenderState renderState;
    protected BoardState gameState;
    protected Rectangle gameBounds;

    protected Texture2D whitePx, box;
    protected Texture2D Deck_Texture, texture_back_card, cardShadow;
    protected SpriteFont font, BaveuseFont, large_font;
    protected ISoundManager<GamePlaySounds> soundManager;
    protected Random random;
    private int previousPlayerIndex;
    private bool endGame = false;

    protected List<GestureSample> cachedGestures;


    public BaseRenderer(Game game)
      : base(game)
    {
      //gameState = game.Services.GetService(typeof(IBoardState)) as IBoardState;
    }

    /// <summary>
    /// Allows the game component to perform any initialization it needs to before starting
    /// to run.  This is where it can query for any required services and load content.
    /// </summary>
    public override void Initialize()
    {
      // Enables input
      //TouchPanel.EnabledGestures = GestureType.DoubleTap | GestureType.Tap | GestureType.FreeDrag | GestureType.DragComplete;

      Game.Services.AddService(typeof(IBoardRenderer), this);

      // Load services
      spriteBatch = Game.GetScopaContext().SpriteBatch;
      var input_services = Game.Services.GetService(typeof(IInputManager)) as IInputManager;
      soundManager = Game.Services.GetService(typeof(ISoundManager<GamePlaySounds>)) as ISoundManager<GamePlaySounds>;
      //input_services.OnTap += new InputManager.Tap(input_services_OnTap);
      //input_services.OnDrag += new InputManager.Tap(input_services_Drag);

      cachedGestures = new List<GestureSample>(10);
      renderState = RenderState.TurnStart;
      random = new Random();

      previousPlayerIndex = gameState.CurrentPlayerIndex;

      base.Initialize();
    }

    protected override void Dispose(bool disposing)
    {
      //TouchPanel.EnabledGestures = GestureType.None;
      Game.Services.RemoveService(typeof(IBoardRenderer));
      base.Dispose(disposing);
    }


    protected override void LoadContent()
    {
      whitePx = Game.Content.Load<Texture2D>("white_pixel");
      box = Game.Content.Load<Texture2D>(@"Images//Game//roundBox");
      font = Game.Content.Load<SpriteFont>("small_font");
      BaveuseFont = Game.Content.Load<SpriteFont>("baveuse_small");
      large_font = Game.Content.Load<SpriteFont>("large_font");

      base.LoadContent();
    }

    public abstract void UpdateIntro(GameTime gameTime);
    public abstract void UpdatePlayerBlocks(GameTime gameTime);
    public abstract void UpdateAnimation(GameTime gameTime);
    public abstract void UpdateTurnStart(GameTime gameTime);
    public abstract void UpdateShowLastTake(GameTime gameTime);
    public abstract void UpdateMain(GameTime gameTime);
    public abstract void ChangeTurn(GameTime gameTime);
    public abstract void UpdateUI(GameTime gameTime);
    public abstract void UpdateRecap(GameTime gameTime);


    /// <summary>
    /// Allows the game component to update itself.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot of timing values.</param>
    public override void Update(GameTime gameTime)
    {
      cachedGestures.Clear();

      while (TouchPanel.IsGestureAvailable)
        cachedGestures.Add(TouchPanel.ReadGesture());

      if (gameState.CurrentPlayerIndex != previousPlayerIndex)
      {
        ChangeTurn(gameTime);
      }

      //if (gameState.Pack.Cards.Count == 0 && gameState.MossaAnimata == true && gameState.MossaFatta == false) // && gameState.BoardBlocks.ElementAt(0).Cards.Count == 0)
      //{

      //  if (gameState.Players.All(p => p.Cards.Count == 0))
      //    endGame = true;
        if (gameState.endGame)
        //if (endGame)
        {
          for (int i = 0; i < gameState.Players.Count; i++)
          {
            for (int j = 0; j < gameState.Players[i].Takes.Count; j++)
            {
              gameState.Players[i].Takes[j].Cards.ForEach(c => c.Depth = 0.9f);
            }
          }
          renderState = RenderState.Recap;
          gameState.endGame = false;
          //endGame = false;
        }
      //}

      previousPlayerIndex = gameState.CurrentPlayerIndex;

      switch (renderState)
      {
        case RenderState.Input:
          // Main game
          // If there are animations to play, change the state to animation
          if (gameState.Animations.Any())
            UpdateAnimation(gameTime);
          if (gameState.Players[gameState.CurrentPlayerIndex].AIType == AIType.Human)
            UpdateMain(gameTime);
          UpdateUI(gameTime);
          break;
        case RenderState.Intro:
          // Game intro
          UpdateIntro(gameTime);
          break;
        case RenderState.Animation:
          // Animations
          //UpdateAnimation(gameTime);
          break;
        case RenderState.ShowLastTake:
          // Show the other players' last takes
          UpdateShowLastTake(gameTime);
          if (gameState.Animations.Any())
            UpdateAnimation(gameTime);
          //UpdateUI(gameTime);
          break;
        case RenderState.TurnStart:
          // Show a button before starting the human turn
          UpdateTurnStart(gameTime);
          if (gameState.Animations.Any())
            UpdateAnimation(gameTime);
          UpdateUI(gameTime);
          break;
        case RenderState.Recap:
          // Show a Recap Screen
          UpdateRecap(gameTime);
          break;
      }

      base.Update(gameTime);
    }


    public abstract void DrawIntro(GameTime gameTime);
    public abstract void DrawBoard(GameTime gameTime);
    public abstract void DrawTableCards(GameTime gameTime);
    public abstract void DrawPlayerBlocks(GameTime gameTime);
    public abstract void DrawAnimation(GameTime gameTime);
    public abstract void DrawTakes(GameTime gameTime);
    public abstract void DrawTurnStart(GameTime gameTime);
    public abstract void DrawUI(GameTime gameTime);
    public abstract void DrawScopa(GameTime gameTime);
    public abstract void DrawScore(GameTime gameTime);


    public override void Draw(GameTime gameTime)
    {
      spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend);

      // These objects are always drawn
      DrawBoard(gameTime);
      DrawTableCards(gameTime);
      DrawPlayerBlocks(gameTime);
      DrawScopa(gameTime);
      DrawUI(gameTime);

      switch (renderState)
      {
        case RenderState.Input:
          // Input state
          break;
        case RenderState.Intro:
          // Game Intro
          DrawIntro(gameTime);
          break;
        case RenderState.Animation:
          // Animations
          DrawAnimation(gameTime);
          break;
        case RenderState.ShowLastTake:
          // Show the other players' takes
          DrawTakes(gameTime);
          break;
        case RenderState.TurnStart:
          // Show a button before starting the human turn
          DrawTurnStart(gameTime);
          break;
        case RenderState.Recap:
          // Show a Level Recap Screen
          DrawScore(gameTime);
          break;
      }
      spriteBatch.End();
      base.Draw(gameTime);
    }

    /// <summary>
    /// A function to draw a string centered in the given position
    /// </summary>
    /// <param name="font">The font to use</param>
    /// <param name="msg">The string to draw</param>
    /// <param name="pos">The centered position of the string</param>
    /// <param name="scale">The scale of the message</param>
    protected void DrawCenteredMsg(SpriteFont font, string msg, Vector2 pos, Color color, Vector2 scale, float rotation, float depth)
    {
      spriteBatch.DrawString(font, msg, pos, color, rotation, font.MeasureString(msg) / 2, scale, SpriteEffects.None, depth);
    }


    /// <summary>
    /// Disegna Carta da gioco su tavolo
    /// </summary>
    /// <param name="c">Carta da disegnare</param>
    protected void DrawCard(Card c)
    {
      Rectangle Card_Rectangle = new Rectangle((int)c.CoordPosition.X, (int)c.CoordPosition.Y, c.Width, c.Height);

      // If the card is being dragged or is turning, draw its shadow
      if (c.IsBeingDragged || c.IsTurning)
      {
        spriteBatch.Draw(cardShadow, new Rectangle(Card_Rectangle.X + 10, Card_Rectangle.Y + 10, c.Width, c.Height), null, Color.Lerp(Color.Black, Color.Transparent, 0.3f), c.Rotation, new Vector2(cardShadow.Width / 2, cardShadow.Height / 2), SpriteEffects.None, c.Depth - 0.015f);
        Card_Rectangle.Inflate(8, 8);
      }

      if (c.IsBeingDragged)
      {
        // Offset the card if it's being dragged
        Card_Rectangle.Offset(-5, -5);
      }

      // Draw card
      if (!c.ShowBack)
        spriteBatch.Draw(Deck_Texture, Card_Rectangle, c.SourceRectangle, Color.White, c.Rotation, new Vector2(c.SourceRectangle.Width / 2, c.SourceRectangle.Height / 2), SpriteEffects.None, c.Depth);
      else
        spriteBatch.Draw(texture_back_card, Card_Rectangle, null, Color.White, c.Rotation, new Vector2(texture_back_card.Width / 2, texture_back_card.Height / 2), SpriteEffects.None, c.Depth);

      // Draw a border
      //Card_Rectangle.Inflate(1,1);
      //Card_Rectangle.Offset(1, 1);
      //spriteBatch.Draw(texture_back_card, Card_Rectangle, null, Color.Black, c.Rotation, new Vector2(texture_back_card.Width / 2, texture_back_card.Height / 2), SpriteEffects.None, c.Depth - 0.01f);

      return;

    } // Fine DrawCard

    public abstract event ReleasePlayerCard ReleasePlayerCard;
    public abstract event OnExit OnExit;
    public abstract event OnMenuPause OnMenuPause;
    public abstract event OnMatchExit OnMatchExit;
  }
}
